﻿#if ENABLE_YOOASETABLES == false
using ABA;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using YooAsset;

namespace JKFrame
{
    public class ResSystemYooAsset:Singleton<ResSystemYooAsset>
    {
        public delegate void AssetLoadComplete<T>(T result);

        public class AssetAOHandle
        {
            public UnityEngine.Object AssetObject;
            public AssetHandle handle;
        }


        #region 普通class对象
        /// <summary>
        /// 获取实例-普通Class
        /// 如果类型需要缓存，会从对象池中获取
        /// 如果对象池没有或返回null
        /// </summary>
        public static T Get<T>() where T : class
        {
            return PoolSystem.GetObject<T>();
        }

        /// <summary>
        /// 获取实例-普通Class
        /// 如果类型需要缓存，会从对象池中获取
        /// 如果对象池没有或返回null
        /// </summary>
        /// <param name="keyName">对象池中的名称</param>
        public static T Get<T>(string keyName) where T : class
        {
            return PoolSystem.GetObject<T>(keyName);
        }
        /// <summary>
        /// 获取实例-普通Class
        /// 如果类型需要缓存，会从对象池中获取
        /// 如果对象池没有或new一个返回
        /// <summary>
        public static T GetOrNew<T>() where T : class, new()
        {
            T obj = PoolSystem.GetObject<T>();
            if (obj == null) obj = new T();
            return obj;
        }


        /// <summary>
        /// 获取实例-普通Class
        /// 如果类型需要缓存，会从对象池中获取
        /// 如果对象池没有或new一个返回
        /// <summary>
        /// <param name="keyName">对象池中的名称</param>
        public static T GetOrNew<T>(string keyName) where T : class, new()
        {
            T obj = PoolSystem.GetObject<T>(keyName);
            if (obj == null) obj = new T();
            return obj;
        }

        /// <summary>
        /// 卸载普通对象，这里是使用对象池的方式
        /// </summary>
        public static void PushObjectInPool(System.Object obj)
        {
            PoolSystem.PushObject(obj);
        }

        /// <summary>
        /// 普通对象（非GameObject）放置对象池中
        /// 基于KeyName存放
        /// </summary>
        public static void PushObjectInPool(object obj, string keyName)
        {
            PoolSystem.PushObject(obj, keyName);
        }

        /// <summary>
        /// 初始化对象池并设置容量
        /// </summary>
        /// <param name="keyName">资源名称</param>
        /// <param name="maxCapacity">容量限制，超出时会销毁而不是进入对象池，-1代表无限</param>
        /// <param name="defaultQuantity">默认容量，填写会向池子中放入对应数量的对象，0代表不预先放入</param>
        public static void InitObjectPool<T>(string keyName, int maxCapacity = -1, int defaultQuantity = 0) where T : new()
        {
            PoolSystem.InitObjectPool<T>(keyName, maxCapacity, defaultQuantity);
        }
        /// <summary>
        /// 初始化对象池并设置容量
        /// </summary>
        /// <param name="maxCapacity">容量限制，超出时会销毁而不是进入对象池，-1代表无限</param>
        /// <param name="defaultQuantity">默认容量，填写会向池子中放入对应数量的对象，0代表不预先放入</param>
        public static void InitObjectPool<T>(int maxCapacity = -1, int defaultQuantity = 0) where T : new()
        {
            PoolSystem.InitObjectPool<T>(maxCapacity, defaultQuantity);
        }
        /// <summary>
        /// 初始化一个普通C#对象池类型
        /// </summary>
        /// <param name="keyName">keyName</param>
        /// <param name="maxCapacity">容量，超出时会丢弃而不是进入对象池，-1代表无限</param>
        public static void InitObjectPool(string keyName, int maxCapacity = -1)
        {
            PoolSystem.InitObjectPool(keyName, maxCapacity);
        }

        /// <summary>
        /// 初始化对象池
        /// </summary>
        /// <param name="type">资源类型</param>
        /// <param name="maxCapacity">容量限制，超出时会销毁而不是进入对象池，-1代表无限</param>
        public static void InitObjectPool(System.Type type, int maxCapacity = -1)
        {
            PoolSystem.InitObjectPool(type, maxCapacity);
        }

#endregion

#region 游戏对象

        /// <summary>
        /// 卸载游戏对象，这里是使用对象池的方式
        /// </summary>
        public static void PushGameObjectInPool(GameObject gameObject)
        {
            PoolSystem.PushGameObject(gameObject);
        }

        /// <summary>
        /// 游戏物体放置对象池中
        /// </summary>
        /// <param name="keyName">对象池中的key</param>
        /// <param name="obj">放入的物体</param>
        public static void PushGameObjectInPool(string keyName, GameObject gameObject)
        {
            PoolSystem.PushGameObject(keyName, gameObject);
        }


        /// <summary>
        /// 初始化一个GameObject类型的对象池类型
        /// </summary>
        /// <param name="keyName">对象池中的标识</param>
        /// <param name="maxCapacity">容量限制，超出时会销毁而不是进入对象池，-1代表无限</param>
        /// <param name="defaultQuantity">默认容量，填写会向池子中放入对应数量的对象，0代表不预先放入</param>
        /// <param name="prefab">填写默认容量时预先放入的对象</param>
        public  void InitGameObjectPool(string keyName, int maxCapacity = -1, string assetPath = null, int defaultQuantity = 0)
        {
            GameObject prefab = LoadAsset<GameObject>(assetPath);
            PoolSystem.InitGameObjectPool(keyName, maxCapacity, prefab, defaultQuantity);
            //UnloadAsset(prefab);
        }

        /// <summary>
        /// 初始化对象池并设置容量
        /// </summary>
        /// <param name="maxCapacity">容量限制，超出时会销毁而不是进入对象池，-1代表无限</param>
        /// <param name="defaultQuantity">默认容量，填写会向池子中放入对应数量的对象，0代表不预先放入</param>
        /// <param name="assetPath">资源路径</param>
        public  void InitGameObjectPool(string assetPath, int maxCapacity = -1, int defaultQuantity = 0)
        {
            GameObject prefab = LoadAsset<GameObject>(assetPath);
            PoolSystem.InitGameObjectPool(prefab, maxCapacity, defaultQuantity);
            //UnloadAsset(prefab);

        }


        /// <summary>
        /// <summary>
        /// 加载游戏物体
        /// 会自动考虑是否在对象池中存在
        /// <param name="assetPath"></param>
        /// <param name="parent"></param>
        /// <param name="keyName"></param>
        public  GameObject InstantiateGameObject(Transform parent, string keyName)
        {
            GameObject go;
            go = PoolSystem.GetGameObject(keyName, parent);
            if (!go.IsNull()) return go;

            GameObject prefab = LoadAsset<GameObject>(keyName);
            if (!prefab.IsNull())
            {
                go = GameObject.Instantiate(prefab, parent);
                go.name = keyName;
                //UnloadAsset(prefab);
            }
            return go;
        }


        /// <summary>
        /// 加载游戏物体
        /// 会自动考虑是否在对象池中存在
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="parent">父物体</param>
        public  GameObject InstantiateGameObject(string assetPath, Transform parent = null, string keyName = null)
        {
            string assetName = GetAssetNameByPath(assetPath);
            GameObject go;
            if (keyName == null) 
                go = PoolSystem.GetGameObject(assetName, parent);
            else 
                go = PoolSystem.GetGameObject(keyName, parent);

            if (!go.IsNull()) 
                return go;

            var obj  = LoadAsset<GameObject>(assetPath);

            go = GameObject.Instantiate(obj, parent);
            go.name = keyName != null ? keyName : assetName;

            //{
            //    if (!obj.IsNull())
            //    {
            //        go = GameObject.Instantiate(obj, parent);
            //        go.name = keyName != null ? keyName : assetName;
            //        //UnloadAsset(prefab);
            //    }
            //});

            return go;
        }

        /// <summary>
        /// 加载游戏物体
        /// 会自动考虑是否在对象池中存在
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="parent">父物体</param>
        public void InstantiateGameObjectAsync(string assetPath,Action<GameObject>cb,Transform parent = null, string keyName = null)
        {
            string assetName = GetAssetNameByPath(assetPath);
            GameObject go;
            if (keyName == null)
                go = PoolSystem.GetGameObject(assetName, parent);
            else
                go = PoolSystem.GetGameObject(keyName, parent);

            if (!go.IsNull())
                return ;

            
            LoadAssetAsync<GameObject>(assetPath, (obj) =>
            {
                if (!obj.IsNull())
                {
                    go = GameObject.Instantiate(GetAssetFromCache<GameObject>(assetPath), parent);
                    go.name = keyName != null ? keyName : assetName;
                    cb?.Invoke(go);
                    //UnloadAsset(prefab);
                }
            });

        }
        public SceneHandle LoadSceneAsync(string sceneName)
        {
            return YooAssets.LoadSceneAsync(sceneName);
        }


        /// <summary>
        /// 加载游戏物体并获取组件
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent">父物体</param>
        public  T InstantiateGameObject<T>(Transform parent, string keyName) where T : UnityEngine.Component
        {
            GameObject go = InstantiateGameObject(parent, keyName);
            if (!go.IsNull())
            {
                return go.GetComponent<T>();
            }
            return null;
        }

        /// <summary>
        /// 加载游戏物体并获取组件
        /// </summary>
        /// <param name="path">资源路径</param>
        /// <param name="parent">父物体</param>
        public T InstantiateGameObject<T>(string assetPath, Transform parent = null, string keyName = null) where T : UnityEngine.Component
        {
            GameObject go = InstantiateGameObject(assetPath, parent, keyName);
            if (!go.IsNull())
            {
                return go.GetComponent<T>();
            }
            return null;
        }

        ///// <summary>
        ///// 异步实例化游戏物体
        ///// </summary>
        //public static void InstantiateGameObjectAsync(string assetPath, Action<GameObject> callBack = null, Transform parent = null, string keyName = null)
        //{
        //    // 切割路径获取实际的资源名称
        //    string assetName = GetAssetNameByPath(assetPath);
        //    GameObject go;
        //    if (keyName == null) go = PoolSystem.GetGameObject(assetName, parent);
        //    else go = PoolSystem.GetGameObject(keyName, parent);
        //    // 对象池有
        //    if (!go.IsNull())
        //    {
        //        callBack?.Invoke(go);
        //        return;
        //    }
        //    // 不通过缓存池
        //    MonoSystem.Start_Coroutine(DoInstantiateGameObjectAsync(assetPath, callBack, parent));
        //}


        ///// <summary>
        ///// 异步实例化游戏物体并获取组件
        ///// </summary>
        ///// <typeparam name="T">游戏物体上的组件</typeparam>
        //public static void InstantiateGameObjectAsync<T>(string assetPath, Action<T> callBack = null, Transform parent = null, string keyName = null) where T : UnityEngine.Component
        //{
        //    string assetName = GetAssetNameByPath(assetPath);
        //    // 缓存字典里面有
        //    GameObject go;
        //    if (keyName == null) go = PoolSystem.GetGameObject(assetName, parent);
        //    else go = PoolSystem.GetGameObject(keyName, parent);
        //    // 对象有
        //    if (!go.IsNull())
        //    {
        //        callBack?.Invoke(go.GetComponent<T>());
        //        return;
        //    }
        //    // 不通过缓存池
        //    MonoSystem.Start_Coroutine(DoInstantiateGameObjectAsync<T>(assetPath, callBack, parent));
        //}

        static IEnumerator DoInstantiateGameObjectAsync(string assetPath, Action<GameObject> callBack = null, Transform parent = null)
        {
            ResourceRequest resourceRequest = Resources.LoadAsync<GameObject>(assetPath);
            yield return resourceRequest;
            GameObject prefab = resourceRequest.asset as GameObject;
            GameObject go = GameObject.Instantiate<GameObject>(prefab);
            go.name = prefab.name;
            //UnloadAsset(prefab);
            callBack?.Invoke(go);
        }
        static IEnumerator DoInstantiateGameObjectAsync<T>(string assetPath, Action<T> callBack = null, Transform parent = null) where T : UnityEngine.Object
        {
            ResourceRequest resourceRequest = Resources.LoadAsync<GameObject>(assetPath);
            yield return resourceRequest;
            GameObject prefab = resourceRequest.asset as GameObject;
            GameObject go = GameObject.Instantiate<GameObject>(prefab);
            go.name = prefab.name;
            //UnloadAsset(prefab);
            callBack?.Invoke(go.GetComponent<T>());
        }
#endregion
#region 游戏Asset



        /// <summary>
        /// 通过path获取资源名称
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static string GetAssetNameByPath(string assetPath)
        {
            return assetPath.Substring(assetPath.LastIndexOf("/") + 1);
        }
        ///// <summary>
        ///// 异步加载Unity资源 AudioClip Sprite GameObject(预制体)
        ///// </summary>
        ///// <typeparam name="T">具体类型</typeparam>
        ///// <param name="path">资源路径</param>
        ///// <param name="callBack">加载完成后的回调</param>
        //public static void LoadAssetAsync<T>(string assetPath, Action<T> callBack) where T : UnityEngine.Object
        //{
        //    MonoSystem.Start_Coroutine(DoLoadAssetAsync<T>(assetPath, callBack));
        //}

        static IEnumerator DoLoadAssetAsync<T>(string assetPath, Action<T> callBack) where T : UnityEngine.Object
        {
            ResourceRequest resourceRequest = Resources.LoadAsync<T>(assetPath);
            yield return resourceRequest;
            callBack?.Invoke(resourceRequest.asset as T);
        }

        /// <summary>
        /// 加载指定路径的所有资源
        /// </summary>
        public static UnityEngine.Object[] LoadAssets(string assetPath)
        {
            return Resources.LoadAll(assetPath);
        }

        /// <summary>
        /// 加载指定路径的所有资源
        /// </summary>
        public static T[] LoadAssets<T>(string assetPath) where T : UnityEngine.Object
        {
            return Resources.LoadAll<T>(assetPath);
        }

        /// <summary>
        /// 卸载资源
        /// </summary>
        public static void UnloadAsset(UnityEngine.Object assetToUnload)
        {
            Resources.UnloadAsset(assetToUnload);
        }


        /// <summary>
        /// 卸载全部未使用的资源
        /// </summary>
        public static void UnloadUnusedAssets()
        {
            Resources.UnloadUnusedAssets();
        }
        #endregion

        public string defaultPackageName = "DefaultPackage";
        // 防止重复加载
        private Dictionary<int, string> loadNameIdCache = new Dictionary<int, string>();
        private Dictionary<string, AssetHandle> loadHandleCache = new Dictionary<string, AssetHandle>();
        public void Initialize()
        {
            YooAssets.Initialize();
        }
        public ResourcePackage GetPackage(string packageName)
        {
            return YooAssets.GetPackage(packageName);
        }
        public void SetDefaultPackage(ResourcePackage package)
        {
            defaultPackageName = package.PackageName;
            YooAssets.SetDefaultPackage(package);
        }
        public void StartOperation(GameAsyncOperation operation)
        {
            YooAssets.StartOperation(operation);
        }

        public InitializationOperation InitPackageAsync(EPlayMode playMode, string packageName, string buildPipeline)
        {
            // 创建资源包裹类
            var package = YooAssets.TryGetPackage(packageName);
            if (package == null)
                package = YooAssets.CreatePackage(packageName);

            // 编辑器下的模拟模式
            InitializationOperation initializationOperation = null;
            if (playMode == EPlayMode.EditorSimulateMode)
            {             
                var simulateBuildResult = EditorSimulateModeHelper.SimulateBuild(buildPipeline,packageName);
                var createParameters = new EditorSimulateModeParameters();
                createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(simulateBuildResult);
                initializationOperation = package.InitializeAsync(createParameters);
            }

            // 单机运行模式
            if (playMode == EPlayMode.OfflinePlayMode)
            {
                var createParameters = new OfflinePlayModeParameters();
                createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
                initializationOperation = package.InitializeAsync(createParameters);
            }

            // 联机运行模式
            if (playMode == EPlayMode.HostPlayMode)
            {
                string defaultHostServer = CommData.GetHostServerURL();
                string fallbackHostServer = CommData.GetHostServerURL();
                IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
                var createParameters = new HostPlayModeParameters();
                createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
                createParameters.CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
                initializationOperation = package.InitializeAsync(createParameters);
            }

            // WebGL运行模式
            if (playMode == EPlayMode.WebPlayMode)
            {
                var createParameters = new WebPlayModeParameters();
                //createParameters.WebFileSystemParameters = FileSystemParameters.CreateDefaultWebFileSystemParameters();
                initializationOperation = package.InitializeAsync(createParameters);
            }

            return initializationOperation;
        }

        // 从缓存中得到资源，如果缓存没有，就返回null
        public T GetAssetFromCache<T>(string name) where T : UnityEngine.Object
        {
            if (loadHandleCache.ContainsKey(name))
            {
                return loadHandleCache[name].AssetObject as T;
            }

            return null;
        }
        // 同步加载方法 按类型加载
        // 最好不要用同步，会卡死主线程
        public T LoadAsset<T>(string name) where T : UnityEngine.Object
        {
            if (loadHandleCache.ContainsKey(name))
            {
                return loadHandleCache[name].AssetObject as T;
            }

            var package = YooAssets.GetPackage(defaultPackageName);
            var hb = package.LoadAssetSync<T>(name);

            loadNameIdCache.Add(hb.AssetObject.GetInstanceID(), name);
            loadHandleCache.Add(name, hb);

            return hb.AssetObject as T;
        }

        //异步回调加载方法  按类型加载
        public void LoadAssetAsync<T>(string name, AssetLoadComplete<T> complete) where T : UnityEngine.Object
        {
            if (loadHandleCache.ContainsKey(name))
            {
                if (complete != null)
                    complete(loadHandleCache[name].AssetObject as T);
                return;
            }

            var package = YooAssets.GetPackage(defaultPackageName);
            package.LoadAssetAsync<T>(name).Completed += (handle) =>
            {
                if (!loadHandleCache.ContainsKey(name))
                {
                    loadHandleCache.Add(name, handle);
                    loadNameIdCache.Add(handle.AssetObject.GetInstanceID(), name);
                }

                if (complete != null)
                    complete(handle.AssetObject as T);
            };
        }

        //异步协程加载方法  按类型加载
        public AssetAOHandle LoadAssetAsync<T>(string name) where T : UnityEngine.Object
        {
            AssetAOHandle assetHandle = new AssetAOHandle();
            if (loadHandleCache.ContainsKey(name))
            {
                assetHandle.AssetObject = loadHandleCache[name].AssetObject as T;
                return assetHandle;
            }
            var package = YooAssets.GetPackage(defaultPackageName);
            var h = package.LoadAssetAsync<T>(name);
            h.Completed += (handle) => {
                if (!loadHandleCache.ContainsKey(name))
                {
                    loadHandleCache.Add(name, handle);
                    loadNameIdCache.Add(handle.AssetObject.GetInstanceID(), name);
                }

                assetHandle.AssetObject = handle.AssetObject;
            };
            assetHandle.handle = h;
            return assetHandle;
        }




        // string name 按名字加载的资源释放接口,通过资源的实例释放
        public void ReleaseAsset(UnityEngine.Object asset)
        {
            var assetInstanceId = asset.GetInstanceID();
            if (loadNameIdCache.ContainsKey(assetInstanceId))
            {
                var name = loadNameIdCache[assetInstanceId];
                loadNameIdCache.Remove(assetInstanceId);

                var handle = loadHandleCache[name];
                handle.Release();
                loadHandleCache.Remove(name);
            }
            // 卸载所有引用计数为零的资源包。
            var package = YooAssets.GetPackage(defaultPackageName);
            package.UnloadUnusedAssetsAsync();
        }
        // string name 按名字加载的资源释放接口,通过加载时的名字释放
        public void ReleaseAsset(string name)
        {
            if (loadHandleCache.ContainsKey(name))
            {
                var handle = loadHandleCache[name];
                var assetInstanceId = handle.AssetObject.GetInstanceID();
                loadNameIdCache.Remove(assetInstanceId);
                handle.Release();
                loadHandleCache.Remove(name);

                // 卸载所有引用计数为零的资源包。
                var package = YooAssets.GetPackage(defaultPackageName);
                package.UnloadUnusedAssetsAsync();
            }
        }
    }

    /// <summary>
    /// 远端资源地址查询服务类
    /// </summary>
    public class RemoteServices : IRemoteServices
    {
        private readonly string _defaultHostServer;
        private readonly string _fallbackHostServer;

        public RemoteServices(string defaultHostServer, string fallbackHostServer)
        {
            _defaultHostServer = defaultHostServer;
            _fallbackHostServer = fallbackHostServer;
        }
        string IRemoteServices.GetRemoteMainURL(string fileName)
        {
            return $"{_defaultHostServer}/{fileName}";
        }
        string IRemoteServices.GetRemoteFallbackURL(string fileName)
        {
            return $"{_fallbackHostServer}/{fileName}";
        }
    }

    /// <summary>
    /// 资源文件流加载解密类
    /// </summary>
    public class FileStreamDecryption : IDecryptionServices
    {
        ///// <summary>
        ///// 同步方式获取解密的资源包对象
        ///// 注意：加载流对象在资源包对象释放的时候会自动释放
        ///// </summary>
        AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
            managedStream = bundleStream;
            return AssetBundle.LoadFromStream(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());
        }

        /// <summary>
        /// 异步方式获取解密的资源包对象
        /// 注意：加载流对象在资源包对象释放的时候会自动释放
        /// </summary>
        AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
            managedStream = bundleStream;
            return AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());
        }

        private static uint GetManagedReadBufferSize()
        {
            return 1024;
        }

    }

    /// <summary>
    /// 资源文件偏移加载解密类
    /// </summary>
    public class FileOffsetDecryption : IDecryptionServices
    {
        /// <summary>
        /// 同步方式获取解密的资源包对象
        /// 注意：加载流对象在资源包对象释放的时候会自动释放
        /// </summary>
        AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            managedStream = null;
            return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());
        }

        /// <summary>
        /// 异步方式获取解密的资源包对象
        /// 注意：加载流对象在资源包对象释放的时候会自动释放
        /// </summary>
        AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
        {
            managedStream = null;
            return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());
        }

        private static ulong GetFileOffset()
        {
            return 32;
        }

    }
    /// <summary>
    /// 资源文件解密流
    /// </summary>
    public class BundleStream : FileStream
    {
        public const byte KEY = 64;

        public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
        {
        }
        public BundleStream(string path, FileMode mode) : base(path, mode)
        {
        }

        public override int Read(byte[] array, int offset, int count)
        {
            var index = base.Read(array, offset, count);
            for (int i = 0; i < array.Length; i++)
            {
                array[i] ^= KEY;
            }
            return index;
        }
    }
}
#endif